home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Info 1994 March
/
Internet Info CD-ROM (Walnut Creek) (March 1994).iso
/
networking
/
ip
/
ka9q
/
alpha.arc
/
HAPN.C
< prev
next >
Wrap
C/C++ Source or Header
|
1988-06-14
|
11KB
|
492 lines
/* Driver for HAPN-1 8273 card
* Jon Bloom, KE3Z; adapted from KA9Q's PC-100 driver
* Modified Rx interrupt routine to prevent lockup
* John Tanner VK2ZXQ 6th Feb 1988
* Adapted back into 871225.9 by KA9Q 15 Feb 1988
*/
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "iface.h"
#include "hapn.h"
#include "ax25.h"
#include "trace.h"
struct hapn hapn[NHAPN];
void ha0vec();
void (*h_handle[])() = { ha0vec };
int16 nhapn;
/* send command to the 8273
* "base" = base port of 8273
* "cmd" = command byte
* "np" = number of parameter bytes
* "p1" = first parameter (parameters are int)
*/
/*VARARGS3*/
static
cmd_8273(base, cmd, np, p1)
int16 base;
int cmd, np, p1;
{
int *p;
while(inportb(base+STA) & CBSY)
;
outportb(base+CMD, cmd);
p = &p1;
while(np--){
while(inportb(base+STA) & CPBF)
;
outportb(base+PAR, *p++);
}
}
/* Start receiver of 8273 */
static
hrxgo(hp)
register struct hapn *hp;
{
cmd_8273(hp->base, GENERAL_RX, 2, hp->bufsiz & 0xff, hp->bufsiz >> 8);
}
/* Interrupt service function. Entered with hapn index
* The "flag" variable is used in this routine to indicate a
* valid TX or RX interrupt. If an invalid interrupt is detected
* the 8273 is reset.
*/
void
haint(dev)
int dev;
{
register struct hapn *hp;
register int16 base;
char flag = 0;
void htxint(),hrxint();
hp = &hapn[dev];
base = hp->base;
/* Check for TX interrupt */
if(inportb(base+STA) & TXINT){
flag = 1; /* Valid interrupt, set flag */
htxint(hp);
}
/* Check for RX interrupt */
if(inportb(base+STA) & RXINT){
flag = 1; /* Valid interrupt, set flag */
hrxint(hp);
}
/* Check for unknown interrupt */
if(!flag){
hp->badint++; /* Increment error counter */
hapn_init(hp); /* Reinitialise the 8273 */
}
}
/* RX interrupt service
* if status register bit "RXIRA" is set, interrupt is final,
* otherwise, interrupt is data request
*/
static void
hrxint(hp)
register struct hapn *hp;
{
register struct mbuf *bp;
register int16 base;
unsigned char results[10];
hp->rxints++;
base = hp->base;
if(inportb(base+STA) & RXIRA){
/* RX result interrupt
* If the result is a good frame 3 bytes need to be read
* If an error has occurred only one byte need to be read
*/
/* Read first result byte and test for good data */
if((results[0]=(inportb(base + RXI))) == 0xe0){
/* Good result; read two more result bytes */
while((inportb(base + STA) & RXIRA) == 0)
;
/* Read second result byte */
results[1] = inportb(base + RXI);
/* Wait for third result byte */
while((inportb(base + STA) & RXIRA) == 0)
;
results[2] = inportb(base + RXI);/* Read it */
/* Since this frame is ok put it on the queue */
enqueue(&hp->rcvq, hp->rcvbuf);
hp->rcvbuf = NULLBUF;
hp->rcvcnt++;
hp->rframes++;
} else {
/* Error termination
* Parse RIC and act accordingly
* Only one result byte returned on error
*/
switch(results[0]){
case CRCERR:
hp->crcerr++;
break;
case ABORT_DET:
hp->aborts++;
break;
case DMA_OVRN:
hp->dmaorun++;
break;
case MEM_OVFL:
hp->toobig++;
break;
case CD_LOSS:
hp->cdloss++;
hapn_init(hp); /* 8273 reset on cd error */
break;
case RX_ORUN:
hp->rxorun++;
break;
}
/* Throw rx buffer contents away to start over */
hp->rcp = hp->rcvbuf->data;
hp->rcvbuf->cnt = 0;
}
/* Restart the receiver */
cmd_8273(base,RX_DISABLE,0);
hrxgo(hp);
} else {
/* RX data interrupt; allocate new rx buffer if none present */
if((bp = hp->rcvbuf) == NULLBUF){
bp = hp->rcvbuf = alloc_mbuf(hp->bufsiz);
if(bp == NULLBUF){
/* No memory available */
hp->nomem++;
cmd_8273(base, RX_DISABLE, 0);
hrxgo(hp);
return;
}
/* Init buffer pointer */
hp->rcp = hp->rcvbuf->data;
}
/* Barf if rx data is more than buffer can hold (should never
* happen since 8273 is also counting bytes).
*/
if(bp->cnt++ >= hp->bufsiz){
hp->toobig++;
cmd_8273(base, RX_DISABLE, 0);
hrxgo(hp);
free_p(bp);
hp->rcvbuf = NULLBUF;
return;
}
/* Store the received byte */
*hp->rcp++ = inportb(base+RXD);
}
}
/* test for busy channel (CD active)
* returns TRUE if channel busy
*/
static int
hcdchk(base)
int16 base;
{
char isav;
isav = disable();
cmd_8273(base, READ_A, 0);
while(!(inportb(base+STA) & CRBF))
;
restore(isav);
return((inportb(base+RES) & CD) != 0);
}
/* TX interrupt service
* if status register bit "TXIRA" is set, interrupt is final,
* otherwise, interrupt is data request
*/
static void
htxint(hp)
register struct hapn *hp;
{
char isav;
register int16 base;
int16 len;
char c;
isav = disable();
hp->txints++;
base = hp->base;
c = 0;
if(inportb(base+STA) & TXIRA){ /* TX result interupt */
hp->tstate = IDLE;
free_p(hp->sndbuf);
hp->sndbuf = NULLBUF;
/* Read result */
while((inportb(base+STA) & (TXINT | TXIRA)) != (TXINT | TXIRA))
;
c = inportb(base+TXI);
/* Test for tx abort */
switch(c & 0x1f){
case DMA_URUN:
hp->t_urun++;
break;
case CTS_LOSS:
hp->ctsloss++;
break;
case ABORT_CMPLT:
hp->taborts++;
break;
}
}
switch(hp->tstate){
case IDLE: /* See if a buffer is ready to be sent */
if((hp->sndbuf = dequeue(&hp->sndq)) == NULLBUF)
break;
case DEFER: /* Busy-channel check */
if(hp->mode == CSMA && (c & 0x1f) != EARLY_TXI){
if(hcdchk(base)){
hp->tstate = DEFER;
break;
}
}
/* Start transmitter */
len = len_mbuf(hp->sndbuf);
cmd_8273(base, TX_FRAME, 2, len & 0xff, len >> 8);
hp->tstate = ACTIVE;
hp->tframes++;
break;
case ACTIVE: /* Get next byte to send */
if(pullup(&hp->sndbuf, &c, 1) != 1){
cmd_8273(base, ABORT_TXF, 0);
hp->tstate = IDLE;
} else
outportb(base+TXD, c);
break;
}
restore(isav);
}
/* Attach a HAPN adaptor to the system
* argv[0]: hardware type, must be "hapn"
* argv[1]: I/O address, e.g. "0x310"
* argv[2]: vector, e.g. "2"
* argv[3]: mode, must be "ax25"
* argv[4]: interface name, e.g. "ha0"
* argv[5]: rx packet buffer size in bytes
* argv[6]: maximum transmission unit in bytes
* argv[7]: channel-access mechanism, "csma" or "full"
*/
int
hapn_attach(argc, argv)
int argc;
char *argv[];
{
register struct interface *if_h;
extern struct interface *ifaces;
struct hapn *hp;
int dev, i;
char isav;
int hapn_init(), hapn_stop(), ax_send(), ax_output(),
hapn_raw();
void dohapn();
void (*getirq())(); /* Getirq is a function returning a pointer to
* a function returning void */
static struct {
char *str;
char type;
} ch_access [] = { "csma", 0, "full", 1 };
if(nhapn >= NHAPN){
printf("Too many HAPN adaptors\n");
return -1;
}
dev = nhapn++;
/* Initialize hardware constants */
hapn[dev].base = htoi(argv[1]);
hapn[dev].vec = htoi(argv[2]);
/* Save original interrupt vector */
hapn[dev].oldvec = getirq(hapn[dev].vec);
/* Set new interrupt vector */
setirq(hapn[dev].vec, h_handle[dev]);
/* Create new interface structure */
if_h = (struct interface *) calloc(1,sizeof(struct interface));
/* Fill interface structure */
if_h->name = malloc((unsigned) strlen(argv[4]) +1);
strcpy(if_h->name, argv[4]);
if_h->mtu = atoi(argv[6]);
if_h->dev = dev;
if_h->recv = dohapn;
if_h->stop = hapn_stop;
if_h->output = ax_output;
if_h->raw = hapn_raw;
if(strcmp(argv[3], "ax25")){
printf("Mode %s unknown for interface %s\n", argv[3], argv[4]);
free(if_h->name);
free((char *) if_h);
return -1;
}
axarp();
if(mycall.call[0] == '\0'){
printf("set mycall first\n");
free(if_h->name);
free((char *) if_h);
return -1;
}
if_h->send = ax_send;
if(if_h->hwaddr == NULLCHAR)
if_h->hwaddr = malloc(sizeof(mycall));
memcpy(if_h->hwaddr,(char *)&mycall,sizeof(mycall));
/* Link the interface into the interface list */
if_h->next = ifaces;
ifaces = if_h;
/* Fill the local data structure */
hp = &hapn[dev];
hp->bufsiz = atoi(argv[5]);
for(i = 0; i < (sizeof ch_access / sizeof ch_access[0]); i++)
if(!strcmp(argv[7], ch_access[i].str))
hp->mode = ch_access[i].type;
/* Initialize the hardware */
isav = disable();
hapn_init(hp);
/* Enable the interrupt */
maskon(hapn[dev].vec);
restore(isav);
return 0;
}
/* initialize the HAPN adaptor */
int
hapn_init(hp)
register struct hapn *hp;
{
register int16 base;
char isav;
isav = disable();
base = hp->base;
/* Reset the 8273 */
outportb(base+RST, 1);
outportb(base+RST, 0);
inportb(base+TXI); /* Clear any old IR contents */
inportb(base+RXI);
/* Select the operating modes */
cmd_8273(base, SET_XFER, 1, 1);
cmd_8273(base, SET_MODE, 1, HDLC | EARLY | PREFRM | FLG_STM);
cmd_8273(base, SET_SERIAL, 1, NRZI);
cmd_8273(base, SET_B, 1, IRQ_ENB | RTS);
cmd_8273(base, RST_B, 1, 0xff ^ RTS);
hrxgo(hp);
restore(isav);
return 0;
}
/* shut down the HAPN adaptor */
int
hapn_stop(iface)
struct interface *iface;
{
int16 dev;
int16 base;
struct hapn *hp;
dev = iface->dev;
hp = &hapn[dev];
base = hp->base;
/* Mask off interrupt input */
maskoff(hp->vec);
/* Restore original interrupt vector */
setirq(hp->vec,hp->oldvec);
/* Reset the 8273 */
outportb(base+RST, 1);
outportb(base+RST, 0);
return 0;
}
/* Display adaptor statistics */
int
dohapnstat()
{
struct hapn *hp;
int i;
if(nhapn == 0){
printf("No HAPN adaptor attached\n");
return 1;
}
for(i = 0; i < nhapn; i++){
hp = &hapn[i];
printf("HAPN %d: rxints: %ld txints: %ld badint: %-5d\r\n", i,
hp->rxints,hp->txints,hp->badint);
printf(" receive - frames: %-5d crcerrs: %-5d aborts: %-5d dmaorun: %-5d\r\n",
hp->rframes,hp->crcerr, hp->aborts, hp->dmaorun);
printf(" - toobig: %-5d dcdloss: %-5d rxorun: %-5d\r\n",
hp->toobig,hp->cdloss,hp->rxorun);
printf(" transmit - frames: %-5d aborts : %-5d uruns : %-5d ctsloss: %-5d\r\n",
hp->tframes,hp->taborts, hp->t_urun, hp->ctsloss);
}
return 0;
}
/* periodically kicked by mainline routine
* process any queued received frames
* kick tx if waiting on busy channel
*/
void
dohapn(iface)
struct interface *iface;
{
struct hapn *hp;
struct mbuf *bp;
hp = &hapn[iface->dev];
/* Process any received frames */
while((bp = dequeue(&hp->rcvq)) != NULLBUF){
hp->rcvcnt--;
dump(iface,IF_TRACE_IN,TRACE_AX25,bp);
ax_recv(iface, bp);
}
/* Test for deferred transmit (CSMA) */
if(hp->tstate == DEFER)
htxint(hp);
}
/* Send raw packet on HAPN interface */
int
hapn_raw(interface,bp)
struct interface *interface;
struct mbuf *bp;
{
struct hapn *hp;
dump(interface,IF_TRACE_OUT,TRACE_AX25,bp);
hp = &hapn[interface->dev];
enqueue(&hp->sndq, bp);
/* See if anything being transmitted */
if(hp->tstate == IDLE)
htxint(hp);
return 0;
}